package com.dashan.servlets;

import static com.dashan.Constants.*;
import static com.dashan.Constants.CENTER_POINT;
import static com.dashan.Constants.CURRENT_ONLY;
import static com.dashan.Constants.NEAREST_N;
import static com.dashan.Constants.USERNAME;
import static com.dashan.servlets.Parameter.Option.Optional;
import static com.dashan.servlets.Parameter.Option.Required;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.dashan.backend.DashanException;
import com.dashan.backend.datastore.DsPosition;
import com.dashan.backend.geosearch.BoundingBox;
import com.dashan.backend.geosearch.GeoSearch;
import com.dashan.backend.util.XmlHelper;


@SuppressWarnings("serial")
public class GeoSearchServlet extends DashanPrivateServlet {

    @Override
    public ArrayList<Parameter> getParameterList() {
        ArrayList<Parameter> res = super.getParameterList();
        res.add(new Parameter(CURRENT_ONLY, Required, Boolean.class, "true if return latest location"));
        res.add(new Parameter(BOUNDING_BOX, Optional, String.class, "\"top, bottom, right, left\""));
        res.add(new Parameter(CENTER_POINT, Optional, String.class, "\"lon, lat\""));
        res.add(new Parameter(NEAREST_N, Optional, Integer.class));
        res.add(new Parameter(RADIUS, Optional, Double.class));
        return res;
    }

    @Override
    public String getUrlPattern() {
        return "/geosearch";
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        TreeMap<String, Parameter> params = loadParamters(req);
        
        List<DsPosition> res = null;
        if (params.get(BOUNDING_BOX).hasValue()) {
            String[] boundingBoxString = params.get(BOUNDING_BOX).stringValue().split(",");
            if (boundingBoxString.length != 4) {
                throw new ServletException("Parameter format error: bounding box should be " +
                		"in the format of 'top, bottom, right, left'");
            }
            
            double top, bottom, right, left;
            try {
                top = Double.parseDouble(boundingBoxString[0]);
                bottom = Double.parseDouble(boundingBoxString[1]);
                right = Double.parseDouble(boundingBoxString[2]);
                left = Double.parseDouble(boundingBoxString[3]);
            } catch (NumberFormatException e) {
                throw new ServletException("Parameter format error: bounding box should be " +
                "in the format of 'top, bottom, right, left'");
            }
            
            BoundingBox box = new BoundingBox(top, bottom, right, left);
            try {
                res = GeoSearch.boundingBoxSearch(box, 
                        params.get(CURRENT_ONLY).booleanValue());
                
            } catch (DashanException e) {
                throw new ServletException(e.getMessage());
            }
        } else if (params.get(CENTER_POINT).hasValue() 
                && (params.get(NEAREST_N).hasValue() || params.get(RADIUS).hasValue())) {
            String[] lonLat = params.get(CENTER_POINT).stringValue().split(",");
            if (lonLat.length != 2) {
                throw new ServletException("Parameter format error: centerpoint should be " +
                "in the format of 'lon, lat'");
            }
            double lon, lat;
            try {
                lon = Double.parseDouble(lonLat[0]);
                lat = Double.parseDouble(lonLat[1]);
            } catch (NumberFormatException e) {
                throw new ServletException("Parameter format error: centerpoint should be " +
                "in the format of 'lon, lat'");
            }
            
            try {
                if (params.get(NEAREST_N).hasValue()) {
                res = GeoSearch.nearestNSearch(lon, lat, 
                        params.get(NEAREST_N).integerValue(), 
                        params.get(CURRENT_ONLY).booleanValue());
                } else if (params.get(RADIUS).hasValue()) {
                    res = GeoSearch.radiusSearch(lon, lat, 
                            params.get(RADIUS).doubleValue(), 
                            params.get(CURRENT_ONLY).booleanValue());
                }
            } catch (DashanException e) {
                throw new ServletException(e.getMessage());
            }
        }
        
        if (res == null) {
            throw new ServletException("Parameter error, Lack of search query.");
        }
        
        Document xmlDoc = XmlHelper.createXmlDocument("response");        
        Element root = xmlDoc.createElement("geoSearchResult");
        for (DsPosition p : res) {
            Element pos = p.getXmlElement(xmlDoc);
            Element userEle = xmlDoc.createElement(USERNAME);
            userEle.appendChild(xmlDoc.createTextNode(p.getUserName()));
            pos.appendChild(userEle);
            root.appendChild(pos);
        }
        xmlDoc.getDocumentElement().appendChild(root);
        resp.getWriter().print(XmlHelper.xml2String(xmlDoc));
    }
}
